All files / web/src/app/api/vision/sessions/[id] route.ts

0% Statements 0/143
0% Branches 0/1
0% Functions 0/1
0% Lines 0/143

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144                                                                                                                                                                                                                                                                                               
import { type NextRequest, NextResponse } from 'next/server'
import { eq } from 'drizzle-orm'
import { db } from '@/db'
import {
  visionTrainingSessions,
  type VisionTrainingSession,
} from '@/db/schema/vision-training-sessions'
import { promises as fs } from 'fs'
import path from 'path'
import { withAuth } from '@/lib/auth/withAuth'

/**
 * Serialize a VisionTrainingSession for JSON response.
 */
function serializeSession(session: VisionTrainingSession) {
  return {
    ...session,
    createdAt: session.createdAt instanceof Date ? session.createdAt.getTime() : session.createdAt,
    trainedAt: session.trainedAt instanceof Date ? session.trainedAt.getTime() : session.trainedAt,
  }
}

/**
 * GET /api/vision/sessions/[id]
 * Get full details of a training session
 */
export const GET = withAuth(async (_request, { params }) => {
  const { id } = (await params) as { id: string }

  try {
    const [session] = await db
      .select()
      .from(visionTrainingSessions)
      .where(eq(visionTrainingSessions.id, id))

    if (!session) {
      return NextResponse.json({ error: 'Session not found' }, { status: 404 })
    }

    return NextResponse.json({ session: serializeSession(session) })
  } catch (error) {
    console.error('Error fetching training session:', error)
    return NextResponse.json({ error: 'Failed to fetch training session' }, { status: 500 })
  }
})

/**
 * PUT /api/vision/sessions/[id]
 * Update session metadata (notes, tags, displayName)
 *
 * Body:
 * - displayName?: string
 * - notes?: string
 * - tags?: string[]
 */
export const PUT = withAuth(async (request, { params }) => {
  const { id } = (await params) as { id: string }

  try {
    const body = await request.json()
    const { displayName, notes, tags } = body

    // Check session exists
    const [existingSession] = await db
      .select()
      .from(visionTrainingSessions)
      .where(eq(visionTrainingSessions.id, id))

    if (!existingSession) {
      return NextResponse.json({ error: 'Session not found' }, { status: 404 })
    }

    // Build update object
    const updates: Partial<VisionTrainingSession> = {}
    if (displayName !== undefined) updates.displayName = displayName
    if (notes !== undefined) updates.notes = notes
    if (tags !== undefined) updates.tags = tags

    if (Object.keys(updates).length === 0) {
      return NextResponse.json({ error: 'No updates provided' }, { status: 400 })
    }

    const [updatedSession] = await db
      .update(visionTrainingSessions)
      .set(updates)
      .where(eq(visionTrainingSessions.id, id))
      .returning()

    return NextResponse.json({ session: serializeSession(updatedSession) })
  } catch (error) {
    console.error('Error updating training session:', error)
    return NextResponse.json({ error: 'Failed to update training session' }, { status: 500 })
  }
})

/**
 * DELETE /api/vision/sessions/[id]
 * Delete a training session and its model files
 */
export const DELETE = withAuth(async (_request, { params }) => {
  const { id } = (await params) as { id: string }

  try {
    // Get session to find model path
    const [session] = await db
      .select()
      .from(visionTrainingSessions)
      .where(eq(visionTrainingSessions.id, id))

    if (!session) {
      return NextResponse.json({ error: 'Session not found' }, { status: 404 })
    }

    // Don't allow deleting the active model without confirmation
    if (session.isActive) {
      return NextResponse.json(
        {
          error: 'Cannot delete active model. Activate a different model first.',
          code: 'ACTIVE_MODEL',
        },
        { status: 409 }
      )
    }

    // Delete model files
    const modelDir = path.join(process.cwd(), 'data/vision-training/models', session.modelPath)
    try {
      await fs.rm(modelDir, { recursive: true, force: true })
      console.log(`Deleted model files at ${modelDir}`)
    } catch (fsError) {
      // Log but don't fail if files don't exist
      console.warn(`Could not delete model files at ${modelDir}:`, fsError)
    }

    // Delete database record
    await db.delete(visionTrainingSessions).where(eq(visionTrainingSessions.id, id))

    return NextResponse.json({ success: true })
  } catch (error) {
    console.error('Error deleting training session:', error)
    return NextResponse.json({ error: 'Failed to delete training session' }, { status: 500 })
  }
})